//    Travel US - For users of travel.usairways.com
//    intended for US Airways employees and their dependents
//    This app will help you track your flights and display
//    Availability, flight status and other non-rev info
//    Copyright (C) 2014  Renats Stozkovs
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

package com.bc004346.travelus;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.FragmentManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.bc004346.travelus.AsyncTasks.CheckinTaskFragment;
import com.bc004346.travelus.AsyncTasks.GetProfileTaskFragment;
import com.bc004346.travelus.AsyncTasks.LoginTaskFragment;
import com.bc004346.travelus.DataAccess.SQLHelper;
import com.bc004346.travelus.Model.Domain.Flight;
import com.bc004346.travelus.Model.Domain.Reservation;
import com.google.analytics.tracking.android.EasyTracker;

import java.util.Date;
import java.util.List;

import static com.bc004346.travelus.NotificationHelper.displayNotification;
import static com.bc004346.travelus.Utility.DAY;
import static com.bc004346.travelus.Utility.HOUR;
import static com.bc004346.travelus.Utility.MINUTE;
import static com.bc004346.travelus.Utility.SHORT_TIME;
import static com.bc004346.travelus.Utility.log;

public class FullscreenActivity
        extends Activity
        implements FlightFragment.OnFlightSelectionChangeListener,
        LoginTaskFragment.TaskCallbacks,
        GetProfileTaskFragment.TaskCallbacks,
        CheckinTaskFragment.TaskCallbacks {

    //<editor-fold desc="Members">

    // async task fragments tags
    public static final String LOGIN_TASK_TAG = "loginTask";
    public static final String PROFILE_TASK_TAG = "profileTask";
    public static final String CHECKIN_TASK_TAG = "checkinTask";

    // activity callbacks
    public static final int LOGIN_REQUEST_CODE = 1000;

    public static final String CHECKIN_INTENT = "com.bc004346.travelus.checkin";
    public static final String PNR_REFRESH_INTENT = "com.bc004346.travelus.pnrRefresh";
    public static final String EXTRA_FLIGHT = "FLIGHT";

    private Menu mMenu;
    private PendingIntent mPendingCheckinIntent;
    private PendingIntent mPendingPnrRefreshIntent;
    private BroadcastReceiver mBroadcastCheckinReceiver;
    private BroadcastReceiver mBroadcastPnrRefreshCheckinReceiver;
    private AlarmManager mAlarmManager;
    private TextView mStatus;
    private ProgressBar mProgressBar;
    private SQLHelper mSqlHelper;
    private FragmentManager mFragmentManager;
    private Context mContext;
    //private NavigationDrawerHelper mNavigationDrawer;
    private NavigationDrawerFragment mNavigationDrawer;

    //</editor-fold>

    //<editor-fold desc="Properties">

    public ProgressBar getProgressBar() {
        if (mProgressBar == null)
            mProgressBar = (ProgressBar) findViewById(R.id.mainActivityProgressBar);
        return mProgressBar;
    }

    public TextView getStatusTextView() {
        if (mStatus == null) {
            mStatus = (TextView) findViewById(R.id.statusTextView);
        }
        return mStatus;
    }

    public SQLHelper getSqlHelper() {
        if (mSqlHelper == null) {
            mSqlHelper = new SQLHelper(getApplicationContext());
        }
        return mSqlHelper;
    }

    //</editor-fold>

    //<editor-fold desc="Activity overrides">

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fullscreen);
        setupUiEvents();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) {
        switch (requestCode) {
            case LOGIN_REQUEST_CODE:
                if (resultCode == RESULT_OK) {
                    refreshReservationData();
                } else {
                    getStatusTextView().setText(getString(R.string.dashboardStatus_LoginFailed));
                }
                break;
        }
    }

    @Override
    protected void onStart() {
        try {
            super.onStart();
            EasyTracker.getInstance(this).activityStart(this);
        } catch (Exception e) {
            log(e, this);
        }
    }

    @Override
    protected void onStop() {
        try {
            super.onStop();
            Utility.onStop(this);
            EasyTracker.getInstance(this).activityStop(this);
        } catch (Exception e) {
            log(e, this);
        }
    }

    @Override
    protected void onDestroy() {
        try {

            mAlarmManager.cancel(mPendingCheckinIntent);
            mAlarmManager.cancel(mPendingPnrRefreshIntent);

            if (mBroadcastCheckinReceiver != null)
                unregisterReceiver(mBroadcastCheckinReceiver);
            if (mBroadcastPnrRefreshCheckinReceiver != null)
                unregisterReceiver(mBroadcastPnrRefreshCheckinReceiver);

        } catch (Exception e) {
            log(e, this);
        }
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.action_item_menu, menu);
        mMenu = menu;
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        try {
            mNavigationDrawer.handleOnOptionsItemSelected(item);

            switch (item.getItemId()) {
                case R.id.menuRefresh:
                    removeReservationTaskFragment();
                    refreshReservationData();
                    break;
            }
        } catch (Exception e) {
            log(e, this);
        }

        return true;
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        mNavigationDrawer.syncState();
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        mNavigationDrawer.handleOnPrepareOptionsMenu(menu);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        mNavigationDrawer.syncState();
        super.onConfigurationChanged(newConfig);
    }

    //</editor-fold>

    //<editor-fold desc="Async Tasks callbacks">

    @Override
    public void onFlightSelectionChanged(Flight flight) {
        try {
            FragmentManager fm = getFragmentManager();
            FlightDetailsFragment descriptionFragment =
                    (FlightDetailsFragment) fm.findFragmentById(R.id.flightDetailsFragment);
            if (descriptionFragment != null)
                descriptionFragment.setFlightDetails(flight);
        } catch (Exception e) {
            log(e, this);
        }
    }

    @Override
    public void onLoginPostExecute(Boolean success, Exception e) {

        if (e != null) {
            getStatusTextView().setText(String.format(getString(R.string.dashboardStatus_LoginFailedExplicit), e.getMessage()));
            log(e, this);
        } else if (success) {
            getStatusTextView().setText("");
            long target = Utility.pnrRefreshDate.getTime() + Long.valueOf(Utility.pnrRefreshInterval) * HOUR;
            if (target <= new Date().getTime())
                refreshReservationData();
            else
                schedulePnrRefresh(new Date(target));
        } else {
            getStatusTextView().setText(getString(R.string.dashboardStatus_LoginFailed));
            startLoginActivity();
        }
    }

    @Override
    public void onGetProfilePostExecute(List<Reservation> reservations, Exception e) {

        try {
            getProgressBar().setProgress(0);

            if (e != null) {
                getStatusTextView().setText(e.getMessage());
                log(e, this);
            } else {
                if (reservations == null) {
                    getStatusTextView().setText(String.format(getString(R.string.dashboardStatus_NoFlightsFound), Utility.daysToSearch));
                    return;
                }

                Utility.name = Utility.getTravelUS().getName();
                Utility.badge = Utility.getTravelUS().getBadge();
                Utility.seniority = Utility.getTravelUS().getSeniority();

                FlightFragment flightFragment = (FlightFragment) mFragmentManager.findFragmentById(R.id.flightFragment);
                if (flightFragment != null)
                    reservations = flightFragment.RefreshFlightList(reservations);
                if (Utility.autoCheckinEnabled)
                    processFlightsReadyForCheckin(reservations);

                getStatusTextView().setText("");
                removeReservationTaskFragment();
                schedulePnrRefresh(new Date(new Date().getTime() + Long.valueOf(Utility.pnrRefreshInterval) * HOUR));
                Utility.pnrRefreshDate = new Date();
            }
        } catch (NumberFormatException e1) {
            log(e1, this);
        }
    }

    @Override
    public void onGetProfileProgressUpdate(String... update) {
        try {
            getProgressBar().setProgress(Integer.valueOf(update[0]));
            String s = update[1];
            getStatusTextView().setText(s);
        } catch (NumberFormatException e) {
            log(e, this);
        }
    }

    @Override
    public void onCheckinPostExecute(Flight flight, Exception e) {

        try {
            String message = null;

            if (e != null) {
                message = String.format(getString(R.string.dashboardStatus_ErrorCheckingIn),
                        flight.toString(),
                        e.toString());
                log(e, this);
            } else if (flight == null)
                message = getString(R.string.dashboardStatus_FlightObjectEmpty);

            else if (flight.isCheckedIn()) {
                message = String.format(getString(R.string.dashboardStatus_CheckedIn),
                        flight.getFlightNumber());
                getSqlHelper().UpdateFlight(flight);
            } else if (!flight.isCheckedIn()) {
                message = String.format(getString(R.string.dashboardStatus_UnableToCheckIn),
                        flight.getFlightNumber());
                scheduleCheckin(flight, HOUR);
            }

            displayNotification(this, message);
            getStatusTextView().setText("");
        } catch (Exception e1) {
            log(e1, this);
        }
    }

    //</editor-fold>

    //<editor-fold desc="Private Methods">

    private void processFlightsReadyForCheckin(List<Reservation> reservations) {

        try {
            if (reservations.size() > 0)
                for (Reservation res : reservations)
                    for (Flight flight : res.getFlights()) {

                        long timeSpan = flight.getDeparture().getTime() - new Date().getTime();
                        log(String.format(getString(R.string.dashboardLog_ScheduleCheckIn),
                                flight, flight.isCheckedIn(), timeSpan));

                        if (timeSpan <= DAY && !flight.isCheckedIn())
                            doCheckin(flight);
                        else if (timeSpan > DAY && timeSpan < DAY * 2 && !flight.isCheckedIn())
                            scheduleCheckin(flight, timeSpan - DAY + MINUTE);
                    }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void doCheckin(Flight flight) {
        try {
            String tag = CHECKIN_TASK_TAG + String.valueOf(flight.getFlightNumber());
            CheckinTaskFragment mCheckinTaskFragment =
                    (CheckinTaskFragment) mFragmentManager.findFragmentByTag(tag);

            // If the Fragment is non-null, then it is currently being
            // retained across a configuration change.
            if (mCheckinTaskFragment == null) {
                mCheckinTaskFragment = new CheckinTaskFragment(Utility.getTravelUS(), flight);
                mFragmentManager.beginTransaction().add(mCheckinTaskFragment, tag).commitAllowingStateLoss();
            }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void scheduleCheckin(Flight flight, long timeSpan) {

        try {
            log(String.format(getString(R.string.dashboardLog_ScheduleCheckInDate),
                    flight.toString(),
                    timeSpan,
                    new Date(new Date().getTime() + timeSpan)));

            if (flight.isCheckInScheduled()) {
                log(String.format(getString(R.string.dashboardLog_CheckInScheduled)));
                return;
            }

            Intent intent = new Intent(CHECKIN_INTENT);
            intent.putExtra(EXTRA_FLIGHT, flight);
            try {
                mPendingCheckinIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime() + timeSpan,
                        mPendingCheckinIntent);
                flight.setIsCheckInScheduled(true);
                getSqlHelper().UpdateFlight(flight);
                String message = String.format(getString(R.string.dashboardToast_SchedulingToCheckIn),
                        flight.getDeparture(),
                        flight.getFlightNumber());
                Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
            } catch (Exception e) {
                e.printStackTrace();
                log(e.toString());
            }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void schedulePnrRefresh(Date date) {

        try {

            if (!Utility.pnrRefreshEnabled) return;

            String message = String.format(getString(R.string.dashboardToast_SchedulingNextPNRRefresh), SHORT_TIME.format(date));

            log(message);

            Intent intent = new Intent(PNR_REFRESH_INTENT);
            try {
                mPendingPnrRefreshIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime() + Integer.valueOf(Utility.pnrRefreshInterval) * HOUR,
                        mPendingPnrRefreshIntent);
                Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
            } catch (Exception e) {
                log(e.toString());
            }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void setupUiEvents() {

        try {
            Utility.onStart(this);

            mContext = getApplicationContext();
            mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);

            setupAsyncLogin();
            setupAsyncPnrRefresh();

            mFragmentManager = getFragmentManager();
            mNavigationDrawer = (NavigationDrawerFragment) mFragmentManager.findFragmentById(R.id.fullscreenDrawer);
            if (mNavigationDrawer != null)
                mNavigationDrawer.init();

            if (Utility.username.isEmpty() || Utility.password.isEmpty())
                startLoginActivity();
            else {
                LoginTaskFragment mLoginTaskFragment = (LoginTaskFragment) mFragmentManager.findFragmentByTag(LOGIN_TASK_TAG);

                // If the Fragment is non-null, then it is currently being
                // retained across a configuration change.
                if (mLoginTaskFragment == null) {
                    getStatusTextView().setText(getString(R.string.dashboardStatus_LoggingIn));
                    mLoginTaskFragment = new LoginTaskFragment(Utility.getTravelUS(), Utility.username, Utility.password);
                    mFragmentManager.beginTransaction().add(mLoginTaskFragment, LOGIN_TASK_TAG).commit();
                }
            }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void setupAsyncPnrRefresh() {
        if (mBroadcastPnrRefreshCheckinReceiver == null) {
            mBroadcastPnrRefreshCheckinReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    refreshReservationData();
                }
            };
            registerReceiver(mBroadcastPnrRefreshCheckinReceiver, new IntentFilter(PNR_REFRESH_INTENT));
        }
    }

    private void setupAsyncLogin() {
        if (mBroadcastCheckinReceiver == null) {
            mBroadcastCheckinReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Flight flight = (Flight) intent.getSerializableExtra(EXTRA_FLIGHT);
                    doCheckin(flight);
                }
            };
            registerReceiver(mBroadcastCheckinReceiver, new IntentFilter(CHECKIN_INTENT));
        }
    }

    private void startLoginActivity() {
        Intent intent = new Intent(this, LoginActivity.class);
        startActivityForResult(intent, LOGIN_REQUEST_CODE);
    }

    private void refreshReservationData() {

        try {
            GetProfileTaskFragment mGetProfileTaskFragment =
                    (GetProfileTaskFragment) mFragmentManager.findFragmentByTag(PROFILE_TASK_TAG);

            toggleMenuState(false);
            // If the Fragment is non-null, then it is currently being
            // retained across a configuration change.
            if (mGetProfileTaskFragment == null) {
                getStatusTextView().setText(getString(R.string.dashboardStatus_RefreshingPNR));
                mGetProfileTaskFragment = new GetProfileTaskFragment(Utility.getTravelUS(), Utility.daysToSearch);
                mFragmentManager.beginTransaction().add(mGetProfileTaskFragment, PROFILE_TASK_TAG).commitAllowingStateLoss();
            }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void toggleMenuState(boolean enabled) {
        try {
            for (int index = 0; index < mMenu.size(); index++) {
                MenuItem item = mMenu.getItem(index);
                item.setEnabled(enabled);
            }
        } catch (Exception e) {
            log(e, this);
        }
    }

    private void removeReservationTaskFragment() {
        try {
            GetProfileTaskFragment mGetProfileTaskFragment =
                    (GetProfileTaskFragment) mFragmentManager.findFragmentByTag(PROFILE_TASK_TAG);

            toggleMenuState(true);
            // If the Fragment is non-null, then it is currently being
            // retained across a configuration change.
            if (mGetProfileTaskFragment != null)
                mFragmentManager.beginTransaction().remove(mGetProfileTaskFragment).commitAllowingStateLoss();

        } catch (Exception e) {
            log(e, this);
        }
    }

    //</editor-fold>
}
